/*
 * T.Raynal - A.Aviles - F.Diallo
 * XML-Scenarios
 * PPA AL5-2
 * December 2012 
 */
package common;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import common.Commons.OPERATORS;
import common.Commons.RESULT;
import common.exception.InvalidEvaluationResultToExpressionException;
import common.exception.InvalidTypeEvaluationException;
import common.exception.UnregisteredOperatorException;
import common.interfaces.IEvaluable;

import model.Operator;
import model.expression.ExpressionHandler;
import model.expression.interfaces.IOperand;
import model.Result;

// TODO: Auto-generated Javadoc
/**
 * Register for allowed operators.
 */
public class OperatorRegister {

	static boolean _isInitialized= false;
	
	static HashMap<String, Operator> _register;
	
	/**
	 * Gets the operator.
	 *
	 * @param Operator name
	 * @return Operator
	 * @throws UnregisteredOperatorException
	 */
	public static Operator getOperator(String id) throws UnregisteredOperatorException
	{
		Operator operator=null;
		
		if (!_isInitialized)
			Initialize();
		
		if ((operator=_register.get(id))!= null)
			return operator;

		throw new UnregisteredOperatorException("Operator "+ id + " unregistered");
	}
	
	/**
	 * Gets the operators.
	 *
	 * @param Operator types
	 * @return Available operators
	 */
	public static List<String> getOperators(OPERATORS type)
	{
		if (!_isInitialized)
			Initialize();
		
		 List<String> register = new ArrayList <String>();
		
		for (Operator operator : _register.values()) {
			if (operator.getType().equals(type))
			{
				register.add(operator.getId());
			}
		}
		
		return register;
	}

	/**
	 * Initialization routine. 
	 * Build the operators.
	 */
	private static void Initialize()
	{
		_register = new HashMap<>();
		
		/////////////////////////////////////////////////LOGICAL OPS/////////////////////////////////////////////////
		
		_register.put("AND", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) {
				return new Result(expressionA.result()&& expressionB.result());
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException {
				 return (Boolean.parseBoolean(expressionA.result()) == true && Boolean.parseBoolean(expressionB.result()) == true)?new Result(true):new Result(false);

			}
		},OPERATORS.LOGICAL,"AND"));
		
		_register.put("OR", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) {
				 return (expressionA.result() == true || expressionB.result() == true)?new Result(true):new Result(false);
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException {
				 return (Boolean.parseBoolean(expressionA.result()) == true || Boolean.parseBoolean(expressionB.result()) == true)?new Result(true):new Result(false);
			}
		},OPERATORS.LOGICAL,"OR"));
		
		/////////////////////////////////////////////////GENERIC OPS/////////////////////////////////////////////////
		_register.put("=", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				 return (expressionA.result() == expressionB.result())?new Result(true):new Result(false);
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				 return (expressionA.result().toString().equals(expressionB.result().toString()))?new Result(true):new Result(false);

			}
		},OPERATORS.GENERIC,"="));
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		
		_register.put("EQUALS", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				 return (expressionA.result() == expressionB.result())?new Result(true):new Result(false);
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				 return (expressionA.result().toString().equals(expressionB.result().toString()))?new Result(true):new Result(false);

			}
		},OPERATORS.GENERIC,"EQUALS"));
		
		
		/////////////////////////////////////////////////ARITMETHICS OPS/////////////////////////////////////////////////
		
		_register.put(">", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use > as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
					
				return ((Double.parseDouble(expressionA.result())>Double.parseDouble(expressionB.result()))?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,">"));
		
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		_register.put("SUPERIOR", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use > as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return ((Double.parseDouble(expressionA.result())>Double.parseDouble(expressionB.result()))?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"SUPERIOR"));
		////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
		_register.put("<", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use < as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return ((Double.parseDouble(expressionA.result())<Double.parseDouble(expressionB.result()))?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"<"));
		
		
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		_register.put("INFERIOR", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use < as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return ((Double.parseDouble(expressionA.result())<Double.parseDouble(expressionB.result()))?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"INFERIOR"));
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		
		_register.put("<=", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use <= as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return (Double.parseDouble(expressionA.result())<Double.parseDouble(expressionB.result()) ||
						Double.parseDouble(expressionA.result())==Double.parseDouble(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"<="));
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		_register.put("INFERIOR_OR_EQUALS", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use <= as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return (Double.parseDouble(expressionA.result())<Double.parseDouble(expressionB.result()) ||
						Double.parseDouble(expressionA.result())==Double.parseDouble(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"INFERIOR_OR_EQUALS"));
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////

		_register.put(">=", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use >= as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return (Double.parseDouble(expressionA.result())>Double.parseDouble(expressionB.result()) ||
						Double.parseDouble(expressionA.result())==Double.parseDouble(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,">="));
		
		/////////////////////////////////////////////////TEMP ALIAS/////////////////////////////////////////////////

		_register.put("SUPERIOR_OR_EQUALS", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use >= as an operator");
				
				if ((!Commons.isNumeric(expressionA.result()) || !Commons.isNumeric(expressionB.result())))
					throw new InvalidTypeEvaluationException ("Expression results must be of numerics");
				
				return (Double.parseDouble(expressionA.result())>Double.parseDouble(expressionB.result()) ||
						Double.parseDouble(expressionA.result())==Double.parseDouble(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.ARITMETIC,"SUPERIOR_OR_EQUALS"));
		
		////////////////////////////////////////////////////////////////////////////////////////////////////////////

		
		
		/////////////////////////////////////////////////DYNAMIC OPS/////////////////////////////////////////////////
		
		_register.put("CONTAIN", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {

				if (!(expressionA.result_t()==RESULT.ALPHANUMERIC) 
						|| !(expressionB.result_t()==RESULT.ALPHANUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use CONTAIN as an operator");
				
				return (expressionA.result().contains(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.DYNAMIC,"CONTAIN"));
		
		_register.put("LIKE", new Operator (new ExpressionHandler() {
			
			@Override
			public Result eval(IOperand expressionA, IOperand expressionB) throws InvalidEvaluationResultToExpressionException {
				throw new InvalidEvaluationResultToExpressionException();
			}
			
			@Override
			public Result eval(IEvaluable expressionA, IEvaluable expressionB) throws InvalidEvaluationResultToExpressionException, InvalidTypeEvaluationException {
		
				if (!(expressionA.result_t()==RESULT.NUMERIC) 
						|| !(expressionB.result_t()==RESULT.NUMERIC))
					throw new InvalidTypeEvaluationException("Non numeric Expression "+expressionA.toString()+ " or "+ expressionB.toString()+ "cant' use LIKE as an operator");
				
				return (expressionA.result().contains(expressionB.result())?
						new Result(true): new Result(false));
			}
		},OPERATORS.DYNAMIC,"LIKE"));
		
		
		
		
		_isInitialized =  true;
	}
	
	
	
	

}
